04. 重置 commit
重置与还原
初看, 重置(reset) 似乎和 还原(revert) 相似,但它们实际上差别很大。还原会创建一个新的 commit,并还原或撤消之前的 commit。但是重置会清除 commit!
⚠️ 重置很危险 ⚠️
一定要谨慎使用 git 的重置功能。这是少数几个可以从仓库中清除 commit 的命令。如果某个 commit 不再存在于仓库中,它所包含的内容也会消失。
为了减轻你的压力,澄清下,git 会在完全清除任何内容之前,持续跟踪大约 30 天。要调用这些内容,你需要使用
git reflog命令。请参阅以下链接以了解详情:
相关 commit 引用
你已经知道可以使用 SHA、标签、分支和特殊的
HEAD
指针引用 commit。有时候这些并不足够,你可能需要引用相对于另一个 commit 的 commit。例如,有时候你需要告诉 git 调用当前 commit 的前一个 commit,或者是前两个 commit。我们可以使用特殊的“祖先引用”字符来告诉 git 这些相对引用。这些字符为:
-
^– 表示父 commit -
~– 表示第一个父 commit
我们可以通过以下方式引用之前的 commit:
- 父 commit – 以下内容表示当前 commit 的父 commit
- HEAD^
- HEAD~
- HEAD~1
- 祖父 commit – 以下内容表示当前 commit 的祖父 commit
- HEAD^^
- HEAD~2
- 曾祖父 commit – 以下内容表示当前 commit 的曾祖父 commit
- HEAD^^^
- HEAD~3
^
和
~
的区别主要体现在通过合并而创建的 commit 中。合并 commit 具有两个父级。对于合并 commit,
^
引用用来表示第一个父 commit,而
^2
表示第二个父 commit。第一个父 commit 是当你运行
git merge
时所处的分支,而第二个父 commit 是被合并的分支。
我们来看一个示例,这样更好理解。这是我的
git log
当前的显示结果:
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
|\
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/
* 1a56a81 Merge branch 'sidebar'
|\
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit
我们来看看如何引用一些之前的 commit。因为
HEAD
指向
9ec05ca
commit:
-
HEAD^是db7e87acommit -
HEAD~1同样是db7e87acommit -
HEAD^^是796ddb0commit -
HEAD~2同样是796ddb0commit -
HEAD^^^是0c5975acommit -
HEAD~3同样是0c5975acommit -
HEAD^^^2是4c9749ecommit(这是曾祖父的 (HEAD^^) 第二个父 commit (^2))
哪一个 commit?
请使用此仓库回答以下练习问题:
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
|\
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/
* 1a56a81 Merge branch 'sidebar'
|\
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit
哪一个 commit?
SOLUTION:
209752aQUESTION:
上道题答的不错,再试试这道题吧!对于相同的仓库,
HEAD~4^2
引用的是哪个 commit?
SOLUTION:
NOTE: The solutions are expressed in RegEx pattern. Udacity uses these patterns to check the given answer
git reset
命令
git reset
命令用来重置(清除)commit:
$ git reset <reference-to-commit>
可以用来:
- 将 HEAD 和当前分支指针移到目标 commit
- 清除 commit
- 将 commit 的更改移到暂存区
- 取消暂存 commit 的更改
git reset 的选项
git 根据所使用选项来判断是清除、暂存之前 commit 的更改,还是取消暂存之前 commit 的更改。这些选项包括:
-
--mixed -
--soft -
--hard
通过动画的形式更容易理解这些选项的工作方式。
Nd016 WebND Ud123 Gitcourse BETAMOJITO L6 17 Soft Vs Medium Vs Hard Walkthrough
💡 备份分支 💡
注意,使用
git reset命令将清除当前分支上的 commit。因此,如果你想跟着操作接下来出现的所有重置操作,需要在当前 commit 上创建一个分支,以便用作备份。
在进行任何重置操作之前,我通常会在最近的 commit 上创建一个
backup分支,因此如果出现错误,我可以返回这些 commit:
$ git branch backup
reset 的
--mixed
选项
我们来看看每个选项。
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
使用上述示例仓库,其中
HEAD
指向
9ec05ca
上的
master
,运行
git reset --mixed HEAD^
会把 commit
9ec05ca
中做出的更改移至工作目录中。
终端显示了使用
--mixed
选项进行重置后的结果。更改未暂存。
💡 回到正常状况💡
如果你在重置任何内容前创建了
backup分支,那么你可以轻松地让master分支指向backup分支所指向的同一 commit。你只需:
- 从工作目录中删除未 commit 的更改
- 将
backup合并到master(这将导致快进合并并使master向上移到和backup一样的点)
$ git checkout -- index.html $ git merge backup
reset 的
--soft
选项
我们使用相同的几个 commit 并看看
--soft
选项的工作方式:
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
运行
git reset --soft HEAD^
会把 commit
9ec05ca
中做出的更改直接移至暂存区。
终端显示了使用
--soft
选项进行重置后的结果。更改被移到暂存区。
reset 的
--hard
选项
最后再看看
--hard
选项(当然并非最不重要选项):
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
运行
git reset --hard HEAD^
将清除 commit
9ec05ca
中做出的更改。
终端显示了使用
--hard
选项进行重置后的结果。更改被清除。
现在该你了!
对于以下仓库:
* e014d91 (HEAD -> master, footer) Add links to social media
* 209752a Improve site heading for SEO
* 3772ab1 Set background color for page
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit
内容在哪?
SOLUTION:
被清除内容在哪 2?
SOLUTION:
暂存区reset 小结
总结下,
git reset
命令被用来清除 commit:
$ git reset <reference-to-commit>
它可以用来:
- 将 HEAD 和当前分支指针移到引用的 commit
-
使用
--hard选项清除 commit -
使用
--soft选项将 commit 的更改移至暂存区 -
使用
--mixed选项取消暂存已被 commit 的更改
我们通常会用到祖先引用来指代之前的 commit。祖先引用包含:
-
^– 表示父 commit -
~– 表示第一个父 commit
进一步研究
- 来自 Git 文档的 git-reset (英)
- 来自 Git Blog 的 Reset Demystified (英)
- 来自 Git Book 的 祖先引用 英 | 中